from gettext import gettext as _

import gtk
import gedit
import re

# Menu item example, insert a new item in the Tools menu
ui_str = """<ui>
  <menubar name="MenuBar">
    <menu name="ToolsMenu" action="Tools">
      <placeholder name="ToolsOps_2">
        <menuitem name="DocstringWizard" action="DocstringWizard"/>
      </placeholder>
    </menu>
  </menubar>
</ui>
"""


class DocstringWizardHelper:
    def __init__(self, plugin, window):
        """
        Stores the binding of plugin to window and inserts the menu
        """
        #print "Plugin created for", window
        self._window = window
        self._plugin = plugin
        # Insert menu items
        self._insert_menu()


    def deactivate(self):
        """
        removes installed menu items and deinitializes
        """
        # Remove any installed menu items
        self._remove_menu()
        #print "Plugin stopped for", self._window
        self._window = None
        self._plugin = None
        self._action_group = None

    def _insert_menu(self):
        """
        Inserts the menu item
        """
        # Get the GtkUIManager
        manager = self._window.get_ui_manager()

        # Create a new action group
        self._action_group = gtk.ActionGroup("DocstringWizardPluginActions")
        self._action_group.add_actions([("DocstringWizard", None, _("docstringWizard"),
                                         None, _("insert docstring stubs"),
                                         self.process)])

        # Insert the action group
        manager.insert_action_group(self._action_group, -1)

        # Merge the UI
        self._ui_id = manager.add_ui_from_string(ui_str)



    def _remove_menu(self):
        """
        removes the menu
        """
        # Get the GtkUIManager
        manager = self._window.get_ui_manager()

        # Remove the ui
        manager.remove_ui(self._ui_id)

        # Remove the action group
        manager.remove_action_group(self._action_group)

        # Make sure the manager updates
        manager.ensure_update()

    def update_ui(self):
        """
        passes the update to the action group
        """
        self._action_group.set_sensitive(self._window.get_active_document() != None)


    def process(self, action):
        """
        Processes the wizardy on the current text
        @param action: the action to be processed
        """
        doc = self._window.get_active_document()
        if not doc:
            return
        text = doc.get_text(doc.get_start_iter(), doc.get_end_iter(), False)
        doc.set_text(docstringWizard(text))


class docstringWizardGeditPlugin(gedit.Plugin):
    def __init__(self):
        """
        create a new instance of the plugin
        """
        gedit.Plugin.__init__(self)
        self._instances = {}

    def activate(self, window):
        """
        create a new instance for a specific window
        @param window: the new window to create the instance for
        """
        self._instances[window] = DocstringWizardHelper(self, window)

    def deactivate(self, window):
        """
        kill the instance of one window
        @param window: the closing window
        """
        self._instances[window].deactivate()
        del self._instances[window]

    def update_ui(self, window):
        """
        pass the update to every instance
        @param window: the updating window
        """
        self._instances[window].update_ui()


# configuration parameters
# method parameters to skip
skip = ["self"]
# number of spaces to insert for identation
ident = 4

def insert(original, new, pos):
    """
    inserts new into original at pos
    @type original: string
    @type new: string
    @type pos: integer
    """
    return original[:pos] + new + original[pos:]

def newline(current_ident):
    """
    returns \n with current_ident + global ident spaces attached
    @type current_ident: integer
    @param current_ident: the identation of the line we are wrapping
    """
    global ident
    res = ""
    for i in range(0,current_ident + ident):
        res += " "
    return "\n" + res

def docstringWizard(text):
    """
    FIXME
    @param text:
    @type text:
    """
    # we have to keep track of how many chars we already inserted because
    # we are modifying the string while processing it
    inserted = 0
    global skip

    # regular expressions
    method_pattern = re.compile('.*\sdef .*.')
    variable_pattern = re.compile('[(].*[)]')
    variable_name_pattern = re.compile(',')
    docstring_pattern = re.compile('\s*"""')

    # find all methods
    method_iter = method_pattern.finditer(text)

    # process each method
    for item in method_iter:
        # the position of the docstring
        # here we have to take into account what has already been inserted
        pos = item.end() + inserted
        # the identation of the method is the distance from the beginning of the
        # line till the "d"
        current_ident = item.group().find("d")
        # test if there already is a docstring: the next non-whitespace should be """
        if docstring_pattern.match(text[pos:]):
            # debug/info notice on the konsole
            #print item.group(), " already has a docstring"
            pass
        else:
            variables = []
            #search vor the (...) section
            vars = variable_pattern.search(item.group())
            vars = vars.group()
            # get rid of the paranthesis
            vars = vars.replace('(','')
            vars = vars.replace(')','')
            # split the variables by commata
            for var in variable_name_pattern.split(vars):
                # get rid of spaces
                var = var.strip()
                # and use only the first word, we get rid of standard values
                var = var.partition(" ")[0]
                # test for exclusion
                if not var in skip:
                    variables.append(var)
            # generate the docstring
            docstring = newline(current_ident) + '\"\"\"' + newline(current_ident)\
                        + 'FIXME' + newline(current_ident)
            # add the @ lines
            for var in variables:
                docstring += '@param '+var+':' + newline(current_ident)\
                            +'@type '+var+':' + newline(current_ident)
            # close it
            docstring += '\"\"\"'

            #insert the docstring into the text
            text = insert(text, docstring, pos)
            # record how many chars have been inserted
            inserted += len(docstring)

    return text
